/*
 * 功能：预约服务层，实现相关服务逻辑
 * 作者：张杨(Sunny)
 * 日期： 2024/11/13 下午4:49
 */
package com.example.springboot.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.springboot.common.Result;
import com.example.springboot.entity.Reserve;
import com.example.springboot.entity.User;
import com.example.springboot.mapper.ReserveMapper;
import com.example.springboot.mapper.UserMapper;
import com.example.springboot.utils.TokenUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class ReserveService extends ServiceImpl<ReserveMapper, Reserve> {

    @Resource
    ReserveMapper reserveMapper;

    @Resource
    UserMapper userMapper;


    /**
     * 创建预约记录
     *
     * @param reserve 预约信息对象
     */
    public Result createReserve(Reserve reserve) {
        User currentUser = TokenUtils.getCurrentUser();
        if (currentUser != null) {
            reserve.setUsername(currentUser.getUsername());
            reserve.setCreateTime(DateUtil.today());
            reserveMapper.insert(reserve);
            return Result.success(); // 返回成功结果
        } else {
            return Result.error("用户未登录");
        }
    }



    /**
     * 处理预约记录，通过和拒绝，并写入处理人
     *
     * @param id     预约记录ID
     * @param params 更新参数
     */
    @Transactional // 开启事务管理，放在类级别
    public Result handleReserve(Integer id, Map<String, Object> params) {
        Reserve reserve = getById(id);
        String state = (String) params.get("state");
        // 更新预约状态
        reserve.setState(state);
        User currentUser = TokenUtils.getCurrentUser();
        if (currentUser != null) {
            // 设置处理人
            reserve.setHandler(currentUser.getUsername());
            reserve.setHandlerTime(DateUtil.today());
            saveOrUpdate(reserve);
            return Result.success(); // 返回成功结果
        } else {
            return Result.error("用户未登录"); // 返回错误结果
        }
    }


    /**
     * 根据用户名和状态查询预约记录
     *
     * @param username 用户名
     * @param state    状态
     * @return 预约记录列表
     */
    public List<Reserve> getReservesByUsernameAndState(String username, String state) {
        QueryWrapper<Reserve> queryWrapper = new QueryWrapper<Reserve>().orderByDesc("reserveTime");
        queryWrapper.eq("username", username).eq("state", state);
        return reserveMapper.selectList(queryWrapper);
    }


    /**
     * 根据处理人和状态查询预约记录
     *
     * @param handler 处理人
     * @param state   状态
     * @return 预约记录列表
     */
    public List<Reserve> getReservesByHandlerAndState(String handler, String state) {
        QueryWrapper<Reserve> queryWrapper = new QueryWrapper<Reserve>().orderByDesc("reserveTime");
        queryWrapper.eq("handler", handler).eq("state", state);
        return reserveMapper.selectList(queryWrapper);
    }


    /**
     * 分页查询预约记录
     *
     * @param pageNum     页码
     * @param pageSize    每页大小
     * @param content     内容
     * @param type        类型
     * @param state       状态
     * @param username    用户名
     * @param createTime  创建时间
     * @param reserveTime 预约时间
     * @param remark      备注
     * @return 分页后的预约记录
     */
    public Page<Reserve> getReservesByPage(Integer pageNum, Integer pageSize, String content, String type, String state, String username, String createTime, String reserveTime, String remark) {
        QueryWrapper<Reserve> queryWrapper = new QueryWrapper<Reserve>().orderByDesc("createTime").orderByDesc("state");
        queryWrapper.like(StrUtil.isNotBlank(content), "content", content);
        queryWrapper.like(StrUtil.isNotBlank(type), "type", type);
        queryWrapper.eq(StrUtil.isNotBlank(username), "username", username);
        queryWrapper.like(StrUtil.isNotBlank(createTime), "createTime", createTime);
        queryWrapper.like(StrUtil.isNotBlank(reserveTime), "reserveTime", reserveTime);
        queryWrapper.like(StrUtil.isNotBlank(remark), "remark", remark);
        queryWrapper.like(StrUtil.isNotBlank(state), "state", state);
        return reserveMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
    }


    /**
     * 导出预约数据为Excel文件
     *
     * @param username 用户名筛选条件
     * @param content  内容筛选条件
     * @param ids      ID筛选条件
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    public void exportData(String username, String content, String ids, HttpServletResponse response) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter(true); // 创建Excel写入器
        List<Reserve> list; // 用于存储查询结果的列表
        QueryWrapper<Reserve> queryWrapper = new QueryWrapper<>(); // 创建查询条件包装器

        // 如果ids参数不为空，则按ID筛选数据
        if (StrUtil.isNotBlank(ids)) {
            List<Integer> idsArr1 = Arrays.stream(ids.split(",")).map(Integer::valueOf).collect(Collectors.toList());
            queryWrapper.in("id", idsArr1);
        } else {
            // 否则按用户名或内容筛选数据
            queryWrapper.like(StrUtil.isNotBlank(username), "username", username);
            queryWrapper.like(StrUtil.isNotBlank(content), "content", content);
        }
        list = reserveMapper.selectList(queryWrapper); // 执行查询
        writer.write(list, true); // 将查询结果写入Excel

        // 设置响应头，指定文件类型和文件名
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename="
                + URLEncoder.encode("预约信息表", "UTF-8") + ".xlsx");

        // 将Excel数据刷新到响应输出流并关闭资源
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            writer.flush(outputStream, true);
        } finally {
            writer.close();
        }
    }


    /**
     * 统计每个处理者(handler)处理的已通过课程预约的数量，并更新到用户表中
     * 同时返回处理的数量
     */
    @Transactional
    public Map<String, Integer> countCurrentStudent() {
        // 创建一个QueryWrapper对象，用于构建SQL查询条件
        QueryWrapper<Reserve> queryWrapper = new QueryWrapper<>();
        // 设置查询条件，选择handler字段，并计算每个handler对应的不同username的数量，按handler分组，并且reserve表中的state字段等于"已通过"
        queryWrapper.eq("state", "已通过").eq("type", "课程");
        queryWrapper.select("handler, COUNT(DISTINCT username)");
        queryWrapper.groupBy("handler");
        // 执行查询，返回结果列表
        List<Map<String, Object>> resultList = reserveMapper.selectMaps(queryWrapper);
        // 创建一个UpdateWrapper对象，用于构建SQL更新条件
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

        // 遍历查询结果列表
        for (Map<String, Object> result : resultList) {
            // 获取当前结果中的handler字段值
            String handler = (String) result.get("handler");
            // 获取当前结果中的COUNT(DISTINCT username)字段值，并转换为Long类型
            Long count = ((Number) result.get("COUNT(DISTINCT username)")).longValue();
            // 设置更新条件，username等于handler，并设置User表中的state字段值为count
            updateWrapper.eq("username", handler).set("state", count.toString());
            // 执行更新操作
            userMapper.update(null, updateWrapper);
            // 清空UpdateWrapper对象，以便下一次循环使用
            updateWrapper.clear();
        }

        // 创建一个新的HashMap对象，并添加一个键值对，键为"handlerCount"，值为resultList的大小，然后返回这个Map
        return new HashMap<String, Integer>() {{
            put("handler", resultList.size());
        }};
    }

    // 导入用户数据的方法
    @Transactional
    public Result importData(MultipartFile file) throws IOException {
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        List<Reserve> reserveList = reader.readAll(Reserve.class);
        // 写入数据到数据库
        try {
            saveBatch(reserveList);
            return Result.success();
        } catch (Exception e) {
            return Result.error("数据批量导入错误");
        }
    }
}
